﻿/**************************************************************
 *
 * 唯一标识：a7a7aede-6ba1-41bf-9420-133438c3e6a0
 * 命名空间：Sgr.Domain.Values
 * 创建时间：2023/7/26 14:58:39
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 * 使用示例:
 *
        public class Money : ValueObject
        {
            public decimal Amount { get; }
            public string Currency { get; }

            public Money(decimal amount, string currency)
            {
                Amount = amount;
                Currency = currency;
            }

            protected override IEnumerable<object> GetEqualityComponents()
            {
                yield return Amount;
                yield return Currency;
            }
        }

        // 使用示例
        var money1 = new Money(100m, "USD");
        var money2 = new Money(100m, "USD");
        var money3 = new Money(100m, "EUR");

        Console.WriteLine(money1 == money2);  // True
        Console.WriteLine(money1 == money3);  // False
        Console.WriteLine(money1 != money3);  // True

        var hash1 = money1.GetHashCode();
        var hash2 = money2.GetHashCode();
        Console.WriteLine(hash1 == hash2);    // True

 *
 *
 **************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sgr.Domain.Values
{
    /// <summary>
    /// 值对象基类
    /// </summary>
    /// <remarks>
    /// 值对象是DDD中的一个概念，表示一个没有标识的对象，其相等性通过其属性值来判断。
    /// 参考：https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects
    /// </remarks>
    public abstract class ValueObject : IEquatable<ValueObject>
    {
        /// <summary>
        /// 获取用于相等性比较的组件
        /// </summary>
        /// <returns>用于确定相等性的值的集合</returns>
        protected abstract IEnumerable<object> GetEqualityComponents();

        /// <summary>
        /// 判断两个值对象是否相等
        /// </summary>
        public static bool operator ==(ValueObject? left, ValueObject? right)
        {
            if (left is null && right is null)
                return true;

            if (left is null || right is null)
                return false;

            return left.Equals(right);
        }

        /// <summary>
        /// 判断两个值对象是否不相等
        /// </summary>
        public static bool operator !=(ValueObject? left, ValueObject? right)
            => !(left == right);

        /// <summary>
        /// 判断当前值对象是否与指定的值对象相等
        /// </summary>
        /// <param name="other">要比较的值对象</param>
        /// <returns>如果两个对象相等则返回true，否则返回false</returns>
        public bool Equals(ValueObject? other)
        {
            if (other is null)
                return false;

            return GetType() == other.GetType() &&
                   GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
        }

        /// <summary>
        /// 判断当前值对象是否与指定的对象相等
        /// </summary>
        /// <param name="obj">要比较的对象</param>
        /// <returns>如果两个对象相等则返回true，否则返回false</returns>
        public override bool Equals(object? obj)
            => obj is ValueObject other && Equals(other);

        /// <summary>
        /// 获取值对象的哈希码
        /// </summary>
        /// <returns>哈希码</returns>
        public override int GetHashCode()
        {
            const int startValue = 17;
            const int multiplier = 59;

            return GetEqualityComponents()
                .Aggregate(startValue, (current, obj) =>
                    current * multiplier + (obj?.GetHashCode() ?? 0));
        }

        /// <summary>
        /// 创建当前值对象的副本
        /// </summary>
        /// <returns>值对象的副本</returns>
        public ValueObject? GetCopy()
        {
            return MemberwiseClone() as ValueObject;
        }
    }
}